﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using System.ComponentModel;

namespace LybrixNursingCRMDesktop.FrameWork
{
    public static class DynamicExpressions
    {
        /// <summary>
        /// Builds Lambda Expression for a where condition using property: Name ,Type ,Value  on T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <param name="operatorType"></param>
        /// <param name="propertyValue"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> GetWhereCond<T>(string propertyName, string operatorType,
                                                                string propertyValue)
        {
            bool isNegated = operatorType.StartsWith("!");
            if (isNegated)
                operatorType = operatorType.Substring(1);
            ParameterExpression parameter = Expression.Parameter(typeof(T), "type");
            MemberExpression property = Expression.Property(parameter, propertyName);

            // Cast propertyValue to correct property type
            TypeConverter td = TypeDescriptor.GetConverter(property.Type);
            ConstantExpression constantValue = Expression.Constant(td.ConvertFromString(propertyValue), property.Type);

            // Check if specified method is an Expression member
            MethodInfo operatorMethod = typeof(Expression).GetMethod(operatorType,
                                                                      new[]
                                                                          {
                                                                              typeof (MemberExpression),
                                                                              typeof (ConstantExpression)
                                                                          });
            Expression expression;
            if (operatorMethod == null)
            {
                // Execute against type members
                MethodInfo method = property.Type.GetMethod(operatorType, new[] { property.Type });
                expression = Expression.Call(property, method, constantValue);
            }
            else
            {
                // Execute the passed operator method (e.g. Expression.GreaterThan)
                expression = (Expression)operatorMethod.Invoke(null, new object[] { property, constantValue });
            }
            if (isNegated)
                expression = Expression.Not(expression);
            return Expression.Lambda<Func<T, bool>>(expression, parameter);
        }

        /// <summary>
        /// Builds Lambda Expression for a where condition using property: Name ,Type ,Value  on T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <param name="operatorType"></param>
        /// <param name="propertyValue"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> GetWhereCond<T>(string parentName, string propertyName,
                                                                string operatorType, string propertyValue)
        {
            bool isNegated = operatorType.StartsWith("!");
            if (isNegated)
                operatorType = operatorType.Substring(1);
            ParameterExpression Parentparameter = Expression.Parameter(typeof(T), "type");
            MemberExpression Parentproperty = Expression.Property(Parentparameter, parentName);
            MemberExpression Childproperty = Expression.Property(Parentproperty, propertyName);

            // Cast propertyValue to correct property type
            TypeConverter td = TypeDescriptor.GetConverter(Childproperty.Type);
            ConstantExpression constantValue = Expression.Constant(td.ConvertFromString(propertyValue),
                                                                   Childproperty.Type);

            // Check if specified method is an Expression member
            MethodInfo operatorMethod = typeof(Expression).GetMethod(operatorType,
                                                                      new[]
                                                                          {
                                                                              typeof (MemberExpression),
                                                                              typeof (ConstantExpression)
                                                                          });
            Expression expression;
            if (operatorMethod == null)
            {
                // Execute against type members
                MethodInfo method = Childproperty.Type.GetMethod(operatorType, new[] { Childproperty.Type });
                expression = Expression.Call(Childproperty, method, constantValue);
            }
            else
            {
                // Execute the passed operator method (e.g. Expression.GreaterThan)
                expression = (Expression)operatorMethod.Invoke(null, new object[] { Childproperty, constantValue });
            }
            if (isNegated)
                expression = Expression.Not(expression);
            return Expression.Lambda<Func<T, bool>>(expression, Parentparameter);
        }

        /// <summary>
        /// Build Lambda Expression to reader the "fieldName" from T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static Expression<Func<T, string>> GetFieldorPropertyCond<T>(string fieldName)
        {
            ParameterExpression x = Expression.Parameter(typeof(T), "x");
            Expression<Func<T, string>> f =
                Expression.Lambda<Func<T, string>>
                    (
                        Expression.PropertyOrField(x, fieldName),
                        new[] { x }
                    );

            return f;
        }
    }
}
